<?php

namespace App\Http\Controllers;

use App\Common\Libs\ArrayHelper;
use App\Consts\ExceptionCodeConst;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class BaseController extends Controller
{

    /** @var array $validate 参数验证器 */
    public $validate = [];

    /** @var object $user */
    public $user = '';

    /** @var string $token */
    public $token = '';

    /** @var $instance */
    public static $instance;

    /** @var $className */
    public static $className;

    /** @var $error */
    private $error;

    /**
     * BaseController constructor.
     */
    public function __construct()
    {
        self::$className = static::class;
    }

    /**
     * get instance
     * @return static
     */
    public static function getInstance()
    {
        if (!empty(self::$instance[self::$className])) {
            return self::$instance[self::$className];
        }
        return self::$instance[self::$className] = new static;
    }

    /**
     * alias for get instance
     * @return static
     */
    public static function Ins()
    {
        return self::getInstance();
    }

    /**
     * init controller before action
     * @return mixed
     */
    /**
     * @param array $parameters
     * @return bool
     * @throws \Exception
     */
    public function init($parameters = [])
    {
        // @INIT Implement __INIT() method.
        $parameters = collect($parameters)->toArray();
        foreach ($parameters as $parameter) {
            foreach ($parameter as $key => $value) {
                $this->{$key} = $value;
            }
        }
        return true;
    }

    /**
     * before action
     * @param array $parameters
     * @return mixed
     * @throws \Exception
     */
    public function beforeAction($parameters = [])
    {
        // validate request
        $this->validate(\request(), $this->validate);
        // init
        return $this->init($parameters);
    }

    /**
     * after action
     * @param array $parameters
     * @return mixed
     */
    public function afterAction($parameters = [])
    {
        return true;
    }

    /**
     * load data
     * @param array $data
     * @return $this
     */
    public function load($data)
    {
        foreach ($data as $prop => $datum) {
            $this->$prop = $datum;
        }
        return $this;
    }

    /**
     * @param string $method
     * @param array $parameters
     * @return \Symfony\Component\HttpFoundation\Response|bool
     */
    public function callAction($method, $parameters)
    {
        try {
            $this->beforeAction($parameters);
            if ($this->error) {
                return $this->error;
            }
            // 去除 request 中间件 多余参数
            if (isset($parameters[0]['user']) && isset($parameters[0]['token'])) {
                unset($parameters[0]['user']);
                unset($parameters[0]['token']);
            }

            //如果接收过来的是 null 转成空字符串
            if (!empty($parameters)) {
                foreach ($parameters[0]->request->all() as $key => $parameter) {
                    if ($parameter === null) {
                        $parameters[0]->request->set($key, '');
                    }
                }
            }
            $result = parent::callAction($method, $parameters);
            $this->afterAction($parameters);
            return $result;
        } catch (\Exception $e) {
            $msg = '';
            $code = $e->getCode();
            if (empty($code) && '#' === substr($e->getMessage(), 0, 1)) {
                $code = explode(' - ', $e->getMessage(), 2);
                $msg = !empty($code[1]) ? $code[1] : false;
                $code = intval(substr($code[0] ? : '', 1));
            }
            $code = is_numeric($code) && $code ? $code : ExceptionCodeConst::CODE_FAIL;
            // prod 环境屏蔽错误信息
            if (!isProd() && !strpos($e->getFile(), 'BaseController')) {
                $msg = false === $msg ? '' : ($msg ?: $e->getMessage());
                if ($msg && false === strpos($msg, ' @file[ ') && false === strpos($e->getFile(), 'helper.func')) {
                    $msg .= ' @file[ ' . $e->getFile() . ' : ' . $e->getLine() . ' ]. ';
                }
            }
            return $this->errorReturn($msg, $code);
        }
    }

    /**
     * get property
     * @param $property
     * @return |null
     */
    public function __get($property)
    {
        if (isset($this->$property)) {
            return $this->$property;
        } else {
            return null;
        }
    }

    /**
     * set property
     * @param $name
     * @param $value
     * @return mixed
     */
    public function __set($name, $value)
    {
        return $this->$name = $value;
    }

    /**
     * 自动验证接口参数
     * @param Request $request
     * @param array $rules
     * @param array $messages
     * @param array $customAttributes
     * @return \Illuminate\Http\JsonResponse|bool
     */
    public function validate(Request $request, array $rules, array $messages = [], array $customAttributes = [])
    {
        $method = $request->route()->getActionMethod();
        if (isset($rules[$method]['rules'])) {
            $messages = isset($rules[$method]['messages']) ? $rules[$method]['messages'] : [];
            $rules = $rules[$method]['rules'];
        }
        if (empty($rules)) return true;
        $validator = Validator::make($request->all(), $rules, $messages, $customAttributes);
        if ($validator->fails()) {
            $validatorMsg = 'validation.' . $validator->errors()->first();
            $errorMsg = lang($validatorMsg);
            if (isProd() && $errorMsg == $validatorMsg) {
                $errorMsg = lang('Illegal Params');
            }
            return $this->error = apiShutdown($errorMsg, ExceptionCodeConst::ILLEGAL_PARAMETER);
        }
        return true;
    }

    /**
     * 过滤签名参数
     * @param array $params
     * @return mixed|null
     */
    public function filterSignParams($params = [])
    {
        $params = $params ?: [];
        return ArrayHelper::remove($params, ['sign', 'timestamp']);
    }

    /**
     * 接口统一返回
     * api result return
     * [!] 与 Error::handle 区别：
     *     - apiReturn     注重返回，可以处理Error返回结果
     *     - Error::handle 注重错误处理
     * [!] 重定向支持，append： $data['__REDIRECT_URL'] = 'https://www.baidu.com';
     * [!] Cookie支持，append： $data['__COOKIES'] = ['token' => 'UUID', 'uid' => '00XP'];
     * @param mixed $data
     * @param int $code
     * @param string $message
     * @return \Illuminate\Http\JsonResponse
     */
    public function apiReturn($data = [], $message = '', $code = ExceptionCodeConst::API_SUCCESS)
    {
        return jsonReturn($data, $code, $message);
    }

    /**
     * 构建 Json 数组
     * @param array $data
     * @param string $message
     * @param int $code
     * @return array
     */
    public function buildJson($data = [], $message = '', $code = ExceptionCodeConst::API_SUCCESS)
    {
        return jsonReturn($data, $code, $message, true);
    }

    /**
     * 错误中断返回
     * @param $message
     * @param int $code
     */
    public function errorReturn($message, $code = ExceptionCodeConst::CODE_FAIL)
    {
        return apiShutdown($message, $code);
    }

    /**
     * 列表数据返回
     * @param $data
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
     */
    public function listReturn($data)
    {
        return jsonReturn([
            'status' => 'success',
            'code' => ExceptionCodeConst::API_SUCCESS,
            'msg' => lang('Success'),
            'result' => true,
            'total' => isset($data['total']) ? $data['total'] : 0,
            'rows' => isset($data['rows']) ? $data['rows'] : [],
        ]);
    }

}
